#include "Hashtable.h"
#include <string.h>
#include <stdio.h>

#define FAIL -1
#define SUCC 0

//strdup for copy value
static char *mystrdup(const char *s)
{
	char *b;
	if(!(b = malloc(strlen(s)+1))) return NULL;
    
	strcpy(b, s);
	return b;
}


//default hash function(can be change from main)
static hash_size default_hashfunc(const char *name)
{
	hash_size hash = 0;
	
	while ( *name ) hash+=(unsigned char)(*name++);

	return hash;
}


//create a hash table
HASHTBL* hashtbl_create(hash_size size, hash_size (*hashfunc)(const char *))
{
	HASHTBL* hashtbl;

    //can't give the memory
	if(!(hashtbl = malloc(sizeof(HASHTBL)))) return NULL;

    //can't give the memory for node
	if(!(hashtbl->nodes = calloc(size, sizeof(ENTRY)))) 
    {
		free(hashtbl);
		return NULL;
	}

    //Set size of the hash table
	hashtbl->size = size;

    //set hash function
	if(hashfunc) 
        hashtbl->hashfunc = hashfunc;
	else 
        hashtbl->hashfunc = default_hashfunc;

	return hashtbl;
}


//insert an ENTRY
int hashtbl_insert(HASHTBL* hashtbl, const char *name, const char *type, int size, int location)
{
	ENTRY* node;
    
    //determin the position
	hash_size hash = hashtbl->hashfunc(name) % hashtbl->size;
    
	node = hashtbl->nodes[hash];
    
    //Check if is ocupied 
	while(node) 
    {
        //if same exist
		if(!strcmp(node->name, name)) 
        {
            //-1 indicate fail
			return FAIL;
		}
        
        //move next
		node = node->next;
	}
    
    //fail allocate the memory
	if(!(node = malloc(sizeof(ENTRY)))) return -1;
    
    //set name
	if(!(node->name = mystrdup(name))) 
    {
		free(node);
		return -1;
	}
    //set type 
	if(!(node->type = mystrdup(type))) 
    {
		free(node);
		return -1;
	}
    //set size
    node->size = size;
    //set location
    node->location = location;
    //insert into the head
	node->next = hashtbl->nodes[hash];
	hashtbl->nodes[hash] = node;
    
	return SUCC;
}

//get an ENTRY from table
ENTRY* hashtbl_get(HASHTBL *hashtbl, const char *name)
{
	ENTRY *node;
    
	hash_size hash = hashtbl->hashfunc(name) % hashtbl->size;
	node = hashtbl->nodes[hash];
    
    //check position
	while(node) 
    {
		if(!strcmp(node->name, name)) 
            return node;
        
		node = node->next;
	}
    
	return NULL;
}

//remove an ENTRY
int hashtbl_remove(HASHTBL *hashtbl, const char *name)
{
	ENTRY *node, *prevnode = NULL;
    
	hash_size hash = hashtbl->hashfunc(name) % hashtbl->size;
    node = hashtbl->nodes[hash];
    
    //find
	while(node) 
    {
		if(!strcmp(node->name, name)) 
        {
			free(node->name);
            free(node->type);
            
            //build the link to the next
			if(prevnode) 
                prevnode->next = node->next;
			else 
                hashtbl->nodes[hash] = node->next;
            
			free(node);
            
			return SUCC;
		}
        
		prevnode = node;
        
		node = node->next;
	}
    
	return FAIL;
}

//free the whole table
void hashtbl_destroy(HASHTBL* hashtbl)
{
	hash_size n;
    
	ENTRY *node, *oldnode;
	
	for(n = 0; n < hashtbl->size; ++n) 
    {
		node = hashtbl->nodes[n];
        
		while(node) 
        {
			free(node->name);
            free(node->type);
            
			oldnode = node;
			node = node->next;
            
			free(oldnode);
		}
	}
    
	free(hashtbl->nodes);
    
	free(hashtbl);
}


//resize the table
int hashtbl_resize(HASHTBL *hashtbl, hash_size size)
{
	HASHTBL newtbl;
	hash_size n;
	ENTRY *node,*next;

	newtbl.size = size;
	newtbl.hashfunc = hashtbl->hashfunc;

	if(!(newtbl.nodes = calloc(size, sizeof(ENTRY)))) return FAIL;

	for(n = 0; n < hashtbl->size; ++n) 
    {
		for(node = hashtbl->nodes[n]; node; node = next) 
        {
			next = node->next;
			hashtbl_insert(&newtbl, node->name, node->type, node->size, node->location);
			hashtbl_remove(hashtbl, node->name);
			
		}
	}

	free(hashtbl->nodes);
    
	hashtbl->size = newtbl.size;
	hashtbl->nodes = newtbl.nodes;

	return 0;
}

//print the ENTRY
void PrintEntry(ENTRY* node)
{
    if(!node)
        ;
    else
        printf("IDENTIFIER NAME: %s\tTYPE: %s\tSIZE: %d\tLOCATION: %d\n", node->name, node->type, node->size, node->location);
    
}
//table traversal
void hashtbl_trav(HASHTBL* hashtbl)
{
    size_t n;
    ENTRY* node;
    
    for(n = 0; n < hashtbl->size; n++) 
    {
		node = hashtbl->nodes[n];
        
		while(node) 
        {
            PrintEntry(node);
            node = node->next;
		}
	}

}


